package com.ts.camera2

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.ImageFormat
import android.graphics.SurfaceTexture
import android.hardware.camera2.CameraAccessException
import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CameraCaptureSession.CaptureCallback
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraDevice
import android.hardware.camera2.CameraManager
import android.hardware.camera2.CaptureRequest
import android.hardware.camera2.TotalCaptureResult
import android.media.Image
import android.media.ImageReader
import android.os.Handler
import android.os.HandlerThread
import android.util.AndroidRuntimeException
import android.util.Log
import android.util.Size
import android.view.Surface
import androidx.core.app.ActivityCompat
import com.ts.camera2.camera.Camera2FrameCallback
import com.ts.camera2.camera.CameraUtil
import java.util.Arrays
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import kotlin.math.abs

class Camera2Wrapper {
//    private val mCameraLock = Semaphore(1)

    private var mSupportCameraIds: Array<String>?=null
   var  mCameraDevice: CameraDevice?=null
    constructor( context: Context){
        this.context=context
        mCamera2FrameCallback=context as Camera2FrameCallback
        initCamera2Wrapper()
    }

    var mCamera2FrameCallback: Camera2FrameCallback?=null

    fun checkCameraOdSupport(cameraId:String):Boolean{
        var isSupported=false;
        mSupportCameraIds?.forEachIndexed { index, id ->
            if (cameraId.equals(id)){
                isSupported=true;
            }
        }
        return isSupported;
    }
    fun updateCameraId(cameraId: String) {
        for (id in mSupportCameraIds!!) {
            if (id == cameraId && mCameraId != cameraId) {
                mCameraId = cameraId
                getCameraInfo(mCameraId)
                stopCamera()
                startCamera()
                break
            }
        }
    }
    val DEFAULT_CAMERA_ID=1;
    var context:Context?=null
    var mCameraManager: CameraManager?=null

    var mCameraId="";
    private fun initCamera2Wrapper() {
        mCameraManager = context?.getSystemService(Context.CAMERA_SERVICE) as CameraManager?
        try {
            mSupportCameraIds = mCameraManager!!.cameraIdList
            if (checkCameraIdSupport(DEFAULT_CAMERA_ID.toString())) {
            } else {
                throw AndroidRuntimeException("Don't support the camera id: " + DEFAULT_CAMERA_ID)
            }
            mCameraId = DEFAULT_CAMERA_ID.toString()
            getCameraInfo(mCameraId)
        } catch (e: CameraAccessException) {
            e.printStackTrace()
        }
    }
    private fun checkCameraIdSupport(cameraId: String): Boolean {
        var isSupported = false
        for (id in mSupportCameraIds!!) {
            if (cameraId == id) {
                isSupported = true
                break
            }
        }
        return isSupported
    }

    private val THRESHOLD = 0.001f
    private var mPreviewSize: Size? = null
    private var mPictureSize:Size? = null
    private var mDefaultPreviewSize = Size(1280, 720)
    private var mDefaultCaptureSize = Size(1280, 720)
    private var mSupportPreviewSize: List<Size>? = null
    private var mSupportPictureSize: List<Size>? = null

    private var mSensorOrientation: Int? = null

    private fun getCameraInfo(cameraId: String) {
        var characteristics: CameraCharacteristics? = null
        try {
            characteristics = mCameraManager!!.getCameraCharacteristics(cameraId)
        } catch (e: CameraAccessException) {
            e.printStackTrace()
        }

        val streamConfigs =
            characteristics!!.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
        if (streamConfigs != null) {
            mSupportPreviewSize = listOf(
                *streamConfigs.getOutputSizes(
                    SurfaceTexture::class.java
                )
            )

            var supportDefaultSize = false
            var sameRatioSize: Size? = null
            var defaultRatio = mDefaultPreviewSize.width * 1.0f / mDefaultPreviewSize.height
            mPreviewSize = mSupportPreviewSize?.get(mSupportPreviewSize?.size?.div(2) ?: 1)
            for (size in mSupportPreviewSize as MutableList<Size>) {
                Log.d(
                    TAG,
                    "initCamera2Wrapper() called mSupportPreviewSize " + size.width + "x" + size.height
                )
                val ratio = size.width * 1.0f / size.height
                if (abs((ratio - defaultRatio).toDouble()) < THRESHOLD) {
                    sameRatioSize = size
                }

                if (mDefaultPreviewSize.width == size.width && mDefaultPreviewSize.height == size.height) {
                    Log.d(TAG, "initCamera2Wrapper() called supportDefaultSize ")
                    supportDefaultSize = true
                    break
                }
            }

            if (supportDefaultSize) {
                mPreviewSize = mDefaultPreviewSize
            } else if (sameRatioSize != null) {
                mPreviewSize = sameRatioSize
            }

            supportDefaultSize = false
            sameRatioSize = null
            defaultRatio = mDefaultCaptureSize.width * 1.0f / mDefaultCaptureSize.height
            mSupportPictureSize =
                Arrays.asList(*streamConfigs.getOutputSizes(ImageFormat.YUV_420_888))
            mPictureSize = mSupportPictureSize?.get(0)
            for (size in mSupportPictureSize as MutableList<Size>) {
                Log.d(
                    TAG,
                    "initCamera2Wrapper() called mSupportPictureSize " + size.width + "x" + size.height
                )
                val ratio = size.width * 1.0f / size.height
                if (abs((ratio - defaultRatio).toDouble()) < THRESHOLD) {
                    sameRatioSize = size
                }

                if (mDefaultCaptureSize.width == size.width && mDefaultCaptureSize.height == size.height) {
                    supportDefaultSize = true
                    break
                }
            }
            if (supportDefaultSize) {
                mPictureSize = mDefaultCaptureSize
            } else if (sameRatioSize != null) {
                mPictureSize = sameRatioSize
            }
        }
        mSensorOrientation = characteristics!!.get(CameraCharacteristics.SENSOR_ORIENTATION)
        Log.d(
            TAG,
            "initCamera2Wrapper() called mSensorOrientation = $mSensorOrientation"
        )
    }

    private var mPreviewSurface: Surface? = null
    private var mOnCaptureImageAvailableListener = ImageReader.OnImageAvailableListener { reader->
        Log.e(TAG,"mOnPreviewImageAvailableListener")
//        val image: Image = reader.acquireLatestImage()
//
//        reader.close()
        Log.e(TAG,"mOnCaptureImageAvailableListener")
    }
    private val mOnPreviewImageAvailableListener =
        ImageReader.OnImageAvailableListener { reader ->
//            reader.acquireLatestImage()
            var image=reader.acquireLatestImage();
            if (image!=null){

                if (image.format == ImageFormat.YUV_420_888) {
                    val planes = image.planes
                    mCamera2FrameCallback?.onPreviewFrame(CameraUtil.YUV_420_888_data(image),reader.width,reader.height)

                }
                Log.e(TAG,reader?.imageFormat.toString()+"----")
                image.close();
            }


        }


    fun startCamera(previewSurface: Surface?) {

        startBackgroundThread()
        if (previewSurface != null) {
//            previewSurface.(mPreviewSize!!.width, mPreviewSize!!.height)

            mPreviewSurface=previewSurface;

        } else {
//            mPreviewImageReader = ImageReader.newInstance(
//                mPreviewSize!!.width,
//                mPreviewSize!!.height,
//                ImageFormat.YUV_420_888,
//                2
//            )
//
//
//            mPreviewImageReader!!.setOnImageAvailableListener(
//                mOnPreviewImageAvailableListener,
//                mBackgroundHandler
//            )
//            mPreviewSurface = mPreviewImageReader!!.surface
        }

        if (mCaptureImageReader == null && mPictureSize != null) {
            mCaptureImageReader = ImageReader.newInstance(
                mPictureSize!!.width,
                mPictureSize!!.height,
                ImageFormat.YUV_420_888,
                2
            )
            mCaptureImageReader?.setOnImageAvailableListener(
                mOnPreviewImageAvailableListener,
                mBackgroundHandler
            )
        }

        openCamera()
    }
    fun startCamera(previewSurfaceTex: SurfaceTexture?) {

        startBackgroundThread()
        if (previewSurfaceTex != null) {
            previewSurfaceTex.setDefaultBufferSize(mPreviewSize!!.width, mPreviewSize!!.height)
            mPreviewSurface = Surface(previewSurfaceTex)
        } else {
            mPreviewImageReader = ImageReader.newInstance(
                mPreviewSize!!.width,
                mPreviewSize!!.height,
                ImageFormat.YUV_420_888,
                2
            )
            mPreviewImageReader!!.setOnImageAvailableListener(
                mOnPreviewImageAvailableListener,
                mBackgroundHandler
            )
            mPreviewSurface = mPreviewImageReader!!.surface
        }

        if (mCaptureImageReader == null && mPictureSize != null) {
            mCaptureImageReader = ImageReader.newInstance(
                mPictureSize!!.width,
                mPictureSize!!.height,
                ImageFormat.YUV_420_888,
                2
            )
            mCaptureImageReader!!.setOnImageAvailableListener(
                mOnCaptureImageAvailableListener,
                mBackgroundHandler
            )
        }

        openCamera()
    }

    fun startCamera(){
        startBackgroundThread()
        if (mPreviewImageReader == null && mPreviewSize != null) {
            mPreviewImageReader = ImageReader.newInstance(
                mPreviewSize!!.width, mPreviewSize!!.height,
                ImageFormat.YUV_420_888, 2
            )
            mPreviewImageReader!!.setOnImageAvailableListener(
                mOnPreviewImageAvailableListener,
                mBackgroundHandler
            )
            mPreviewSurface = mPreviewImageReader!!.surface
        }
        if (mCaptureImageReader == null && mPictureSize != null) {
            mCaptureImageReader = ImageReader.newInstance(
                mPictureSize!!.width,
                mPictureSize!!.height,
                ImageFormat.YUV_420_888,
                2
            )
            mCaptureImageReader!!.setOnImageAvailableListener(
                mOnCaptureImageAvailableListener,
                mBackgroundHandler
            )
        }
        openCamera()
    }


    fun stopCamera(){
        mPreviewImageReader?.setOnImageAvailableListener(null, null)

        if (mCaptureImageReader != null) {
            mCaptureImageReader!!.setOnImageAvailableListener(null, null)
        }
        closeCamera()
        stopBackGroundThread()
    }

    fun closeCamera() {
        Log.d(TAG, "closeCamera() called")
        try {
            mCameraLock.acquire()
            if (null != mCameraCaptureSession) {
                mCameraCaptureSession!!.close()
                mCameraCaptureSession = null
            }
            if (null != mCameraDevice) {
                mCameraDevice!!.close()
                mCameraDevice = null
            }
            if (null != mPreviewImageReader) {
                mPreviewImageReader?.close()
                mPreviewImageReader = null
            }

            if (null != mCaptureImageReader) {
                mCaptureImageReader!!.close()
                mCaptureImageReader = null
            }
        } catch (e: InterruptedException) {
            throw RuntimeException("Interrupted while trying to lock camera closing.", e)
        } finally {
            mCameraLock.acquire()
        }
    }
    var surface:Surface?=null
    fun createPreviewRequest(): CaptureRequest? {
        if (null == mCameraDevice || mPreviewSurface == null) return null
        try {
            val builder = mCameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
            builder.addTarget(mPreviewSurface!!)
            builder.addTarget(mCaptureImageReader?.surface!!)
            return builder.build()
        } catch (e: CameraAccessException) {
            Log.e(TAG, e.message!!)
            return null
        }
    }
    private var mPreviewRequest: CaptureRequest? = null
    var mCameraCaptureSession: CameraCaptureSession?=null
    private val mSessionStateCallback: CameraCaptureSession.StateCallback =
        object : CameraCaptureSession.StateCallback() {
            override fun onConfigured(session: CameraCaptureSession) {
                mCameraCaptureSession = session
                try {
                    mPreviewRequest = createPreviewRequest()
                    if (mPreviewRequest != null) {
                        session.setRepeatingRequest(mPreviewRequest!!, null, mBackgroundHandler)
                    } else {
                        Log.e(TAG, "captureRequest is null")
                    }
                } catch (e: CameraAccessException) {
                    Log.e(TAG, "onConfigured $e")
                }
            }

            override fun onConfigureFailed(session: CameraCaptureSession) {
                Log.e(TAG, "onConfigureFailed")
            }
        }
    private var mPreviewImageReader: ImageReader? =
        null
    private var mCaptureImageReader:android.media.ImageReader? = null
    fun capture(){
        var captureBuilder=mCameraDevice?.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
        mCaptureImageReader?.surface?.let { captureBuilder?.addTarget(it) }
        // Use the same AE and AF modes as the preview.
        captureBuilder!!.set(
            CaptureRequest.CONTROL_AF_MODE,
            CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
        )


        // Orientation
        val CaptureCallback
                : CaptureCallback = object : CaptureCallback() {
            override fun onCaptureCompleted(
                session: CameraCaptureSession,
                request: CaptureRequest,
                result: TotalCaptureResult
            ) {
                Log.e(TAG,"CaptureRequest")

                if (mPreviewRequest != null && mCameraCaptureSession != null) {
                    try {
                        mCameraCaptureSession!!.setRepeatingRequest(
                            mPreviewRequest!!,
                            null,
                            mBackgroundHandler
                        )
                    } catch (e: CameraAccessException) {
                        e.printStackTrace()
                    }
                }
            }
        }

        mCameraCaptureSession?.stopRepeating()
        mCameraCaptureSession?.abortCaptures()
        mCameraCaptureSession?.capture(captureBuilder.build(), CaptureCallback, null)
    }
    fun createCaptureSession(){
        try {
            if (null == mCameraDevice || null == mPreviewSurface || null == mCaptureImageReader) return
            mCameraDevice?.createCaptureSession(
                listOf(mPreviewSurface, mCaptureImageReader?.surface),
                mSessionStateCallback, mBackgroundHandler
            )
        } catch (e: CameraAccessException) {
            Log.e(TAG, "createCaptureSession $e")
        }
    }

    var mStateCallBack=object: CameraDevice.StateCallback(){
        override fun onOpened(camera: CameraDevice) {
            mCameraLock.release()
            mCameraDevice=camera;
            createCaptureSession();
            Log.e(TAG,"onOpened")
        }

        override fun onDisconnected(camera: CameraDevice) {
            mCameraLock.release()
            mCameraDevice?.close()
            Log.e(TAG,"onDisconnected")
        }

        override fun onError(camera: CameraDevice, error: Int) {
            mCameraLock.release()
            Log.e(TAG, "error$error")
        }

    }
    private val mCameraLock = Semaphore(1)

    private  val TAG = "Camera2Wrapper"
    private fun openCamera() {

        if (context?.let {
                ActivityCompat.checkSelfPermission(
                    it,
                    Manifest.permission.CAMERA
                )
            } != PackageManager.PERMISSION_GRANTED
        ) {
            return
        }
        var cameraManager=context?.getSystemService(Context.CAMERA_SERVICE) as CameraManager
        if (!mCameraLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
            throw java.lang.RuntimeException("Time out waiting to lock camera opening.")
        }

        cameraManager.openCamera(mCameraId,mStateCallBack,mBackgroundHandler)
    }

    private var mBackgroundHandler:Handler?=null
    private var mBackgroundThread: HandlerThread? = null
    private fun startBackgroundThread() {
        mBackgroundThread = HandlerThread("Camera2Background")
        mBackgroundThread!!.start()
        mBackgroundHandler = Handler(mBackgroundThread!!.looper)
    }


    private fun stopBackGroundThread(){
        mBackgroundThread?.quitSafely()
        mBackgroundThread?.join();
        mBackgroundHandler=null
        mBackgroundHandler=null

    }

    fun getCameraId(): String {
        return mCameraId
    }

    fun getSupportCameraIds(): Array<String> {
        return mSupportCameraIds!!
    }

    fun getSupportPreviewSize(): List<Size> {
        return mSupportPreviewSize!!
    }

    fun getSupportPictureSize(): List<Size> {
        return mSupportPictureSize!!
    }

    fun getPreviewSize(): Size {
        return mPreviewSize!!
    }

    fun getPictureSize(): Size {
        return mPictureSize!!
    }

    fun getSensorOrientation(): Int {
        return mSensorOrientation!!
    }
}